/**
 * Copyright (c) 2005-2012 https://github.com/zhangkaitao
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package com.tyf.common.dao.callback;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.tyf.common.domain.search.SearchOperator;
import com.tyf.common.domain.search.Searchable;
import com.tyf.common.domain.search.filter.AndCondition;
import com.tyf.common.domain.search.filter.DtlCondition;
import com.tyf.common.domain.search.filter.SearchFilter;
import com.tyf.common.domain.search.filter.SearchFilterHelper;
import com.tyf.common.util.CommonUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.StringUtils;

import com.tyf.common.domain.search.filter.Condition;
import com.tyf.common.domain.search.filter.OrCondition;
import com.tyf.common.util.Collections3;
import com.tyf.common.vo.DetailSearchVO;
import com.tyf.common.vo.DetailSearchVO.DetailSearchInnerVO;

/**
 * <p>User: Zhang Kaitao
 * <p>Date: 13-1-16 下午4:20
 * <p>Version: 1.0
 */
public class DefaultSearchCallback implements SearchCallback {

    private static final String paramPrefix = "param_";

    private String alias;
    private String aliasWithDot;
    private static final String DTL_SEARCH_QUERY_STRING = " and %s in (select distinct %s from %s d where 0<>1 %s )";	//去除无用关联
//    private static final String DEFAULT_ALIASWITHDOT = "x.";
    
    public DefaultSearchCallback() {
        this("");
    }

    public DefaultSearchCallback(String alias) {
        this.alias = alias;
        if (!StringUtils.isEmpty(alias)) {
            this.aliasWithDot = alias + ".";
        } else {
            this.aliasWithDot = "";
        }
    }

    public String getAlias() {
        return alias;
    }

    public String getAliasWithDot() {
        return aliasWithDot;
    }

    @Override
    public void prepareQL(StringBuilder ql, Searchable search) {
    	prepareQLOrSQL(ql, search, false);
    }
    
    /**
     * 
     * @param qlOrSql
     * @param search
     * @param isSQL 是否为原生SQL
     * @return 参数个数
     */
    private int prepareQLOrSQL(StringBuilder qlOrSql, Searchable search, boolean isSQL) {
        if (!search.hasSearchFilter()) {
            return 0;
        }

        int paramIndex = 1;
        for (SearchFilter searchFilter : search.getSearchFilters()) {

            if (searchFilter instanceof Condition) {
                Condition condition = (Condition) searchFilter;
                if (condition.getOperator() == SearchOperator.custom) {
                    continue;
                }
            }

		    //DtlCondition的条件已通过prepareDetailSearch处理,直接跳过
	    	if(searchFilter instanceof DtlCondition){
	    		continue;
	    	}
	    	
	    	qlOrSql.append(" and ");
            paramIndex = genCondition(qlOrSql, paramIndex, searchFilter, isSQL);

        }
        return paramIndex;
    }

    private int genCondition(StringBuilder qlOrSql, int paramIndex, SearchFilter searchFilter, boolean isSQL) {
        boolean needAppendBracket = searchFilter instanceof OrCondition || searchFilter instanceof AndCondition;

        if (needAppendBracket) {
            qlOrSql.append("(");
        }

        if (searchFilter instanceof Condition) {
            Condition condition = (Condition) searchFilter;
            //自定义条件
            String entityProperty = condition.getEntityProperty();
            String operatorStr = condition.getOperatorStr();
            //实体名称
            qlOrSql.append(getAliasWithDot());
            if(isSQL){	//如果为sql，则自动转换为下划线格式
            	qlOrSql.append(CommonUtils.convertJaveBeanStrToUnderLine(entityProperty));
            }else{
            	qlOrSql.append(entityProperty);
            }
            //操作符
            //1、如果是自定义查询符号，则使用SearchPropertyMappings中定义的默认的操作符
            qlOrSql.append(" ");
            qlOrSql.append(operatorStr);
            qlOrSql.append(" ");
            
            //是否需要括号将参数值包围
            boolean isNeedAround = false;
            Object value = condition.getValue();
            //如果为集合元素，并且为合计sql，需要添加括号
            if(value instanceof List || value.getClass().isArray()){
            	isNeedAround = true;
            }
            
            if (!condition.isUnaryFilter()) {
            	if(isNeedAround){
            		qlOrSql.append("(");
            	}
            	qlOrSql.append(":");
                qlOrSql.append(paramPrefix);
                qlOrSql.append(paramIndex++);
                if(isNeedAround){
                	qlOrSql.append(")");
                }
                return paramIndex;
            }
        } else if (searchFilter instanceof OrCondition) {
            boolean isFirst = true;
            for (SearchFilter orSearchFilter : ((OrCondition) searchFilter).getOrFilters()) {
                if (!isFirst) {
                    qlOrSql.append(" or ");
                }
                paramIndex = genCondition(qlOrSql, paramIndex, orSearchFilter, isSQL);
                isFirst = false;
            }
        } else if (searchFilter instanceof AndCondition) {
            boolean isFirst = true;
            for (SearchFilter andSearchFilter : ((AndCondition) searchFilter).getAndFilters()) {
                if (!isFirst) {
                    qlOrSql.append(" and ");
                }
                paramIndex = genCondition(qlOrSql, paramIndex, andSearchFilter, isSQL);
                isFirst = false;
            }
        }

        if (needAppendBracket) {
            qlOrSql.append(")");
        }
        return paramIndex;
    }


    @Override
    public int setValues(Query query, Searchable search) {

        int paramIndex = 1;
        
        for (SearchFilter searchFilter : search.getSearchFilters()) {
            paramIndex = setValues(query, searchFilter, paramIndex);
        }
        
        return paramIndex;
    }

    private int setValues(Query query, SearchFilter searchFilter, int paramIndex) {
        if (searchFilter instanceof Condition) {

            Condition condition = (Condition) searchFilter;
            if (condition.getOperator() == SearchOperator.custom) {
                return paramIndex;
            }
            if (condition.isUnaryFilter()) {
                return paramIndex;
            }
            query.setParameter(paramPrefix + paramIndex++, formtValue(condition, condition.getValue()));

        } else if (searchFilter instanceof OrCondition) {

            for (SearchFilter orSearchFilter : ((OrCondition) searchFilter).getOrFilters()) {
                paramIndex = setValues(query, orSearchFilter, paramIndex);
            }

        } else if (searchFilter instanceof AndCondition) {
            for (SearchFilter andSearchFilter : ((AndCondition) searchFilter).getAndFilters()) {
                paramIndex = setValues(query, andSearchFilter, paramIndex);
            }
        } else if (searchFilter instanceof DtlCondition) {
            for (SearchFilter dtlSearchFilter : ((DtlCondition) searchFilter).getDtlFilters()) {
                paramIndex = setValues(query, dtlSearchFilter, paramIndex);
            }
        }
        return paramIndex;
    }
    
    @Override
    public int setValuesForHibernateQ(org.hibernate.Query query, Searchable search) {
        int paramIndex = 1;
        
        for (SearchFilter searchFilter : search.getSearchFilters()) {
            paramIndex = setValuesForHibernateQ(query, searchFilter, paramIndex);
        }
        
        return paramIndex;
    }
    
    @SuppressWarnings("rawtypes")
    private int setValuesForHibernateQ(org.hibernate.Query query, SearchFilter searchFilter, int paramIndex) {
        if (searchFilter instanceof Condition) {

            Condition condition = (Condition) searchFilter;
            if (condition.getOperator() == SearchOperator.custom) {
                return paramIndex;
            }
            if (condition.isUnaryFilter()) {
                return paramIndex;
            }
            Object inputValue = condition.getValue();
            //如果为集合元素,则使用setParameterList赋值;否则使用setParameter
            if(inputValue instanceof List || inputValue.getClass().isArray()){
				List list = null;
        		if(inputValue instanceof List){
        			list = (List)inputValue;
        		}else{
        			list = Arrays.asList((Object[])inputValue);
        		}
        		query.setParameterList(paramPrefix + paramIndex++, list);
            }else{
            	query.setParameter(paramPrefix + paramIndex++, formtValue(condition, condition.getValue()));
            }
        } else if (searchFilter instanceof OrCondition) {

            for (SearchFilter orSearchFilter : ((OrCondition) searchFilter).getOrFilters()) {
                paramIndex = setValuesForHibernateQ(query, orSearchFilter, paramIndex);
            }

        } else if (searchFilter instanceof AndCondition) {
            for (SearchFilter andSearchFilter : ((AndCondition) searchFilter).getAndFilters()) {
                paramIndex = setValuesForHibernateQ(query, andSearchFilter, paramIndex);
            }
        } else if (searchFilter instanceof DtlCondition) {
            for (SearchFilter dtlSearchFilter : ((DtlCondition) searchFilter).getDtlFilters()) {
                paramIndex = setValuesForHibernateQ(query, dtlSearchFilter, paramIndex);
            }
        }
        return paramIndex;
    }

    private Object formtValue(Condition condition, Object value) {
        SearchOperator operator = condition.getOperator();
        if (operator == SearchOperator.like || operator == SearchOperator.notLike) {
            return "%" + value + "%";
        }
        if (operator == SearchOperator.prefixLike || operator == SearchOperator.prefixNotLike) {
            return value + "%";
        }

        if (operator == SearchOperator.suffixLike || operator == SearchOperator.suffixNotLike) {
            return "%" + value;
        }
        return value;
    }
    
    private Object formtValueBySptype(String spTye, Object value) {
        if (spTye.equals(SearchOperator.like.getSymbol()) || spTye.equals(SearchOperator.notLike.getSymbol())) {
            return "%" + value + "%";
        }
        if (spTye.equals(SearchOperator.prefixLike.getSymbol()) || spTye.equals(SearchOperator.prefixNotLike.getSymbol())) {
            return value + "%";
        }

        if (spTye.equals(SearchOperator.suffixLike.getSymbol()) || spTye.equals(SearchOperator.suffixNotLike.getSymbol())) {
            return "%" + value;
        }
        return value;
    }

    public void setPageable(Query query, Searchable search) {
        if (search.hasPageable()) {
            Pageable pageable = search.getPage();
            query.setFirstResult(pageable.getOffset());
            query.setMaxResults(pageable.getPageSize());
        }
    }

    public void prepareOrder(StringBuilder ql, Searchable search) {
    	prepareQlOrSqlOrder(ql, search, false);
    }

	public void prepareDetailSearch(StringBuilder ql, Searchable search, EntityManager entityManager) {
    	if (search.hasDetailSearch()) {
    		StringBuilder detailSearchQl = search.getDetailSearchQl();
    		//如果detailSearchQl已拼接过一次，则直接使用，避免重复拼接
    		if(detailSearchQl==null || detailSearchQl.length()==0){
    			detailSearchQl = new StringBuilder(60);
	    		DetailSearchVO mvo = search.getDetailSearch();
	    		String mIdName = mvo.getIdName();
	    		Map<String, DetailSearchInnerVO> details = mvo.getContents();
	    		
	    		int paramIndex = search.getSearchFilters().size()+1;
	    		for(Map.Entry<String, DetailSearchInnerVO> entry : details.entrySet()){
	    			DetailSearchInnerVO dvo = entry.getValue();
	    			String detailName = StringUtils.capitalize(CommonUtils.convertUnderLineStrToJaveBean(entry.getKey()));	//前端传入name为下划线表名
	    			String dIdName = dvo.getIdName();
	    			Map<String, Object> params = dvo.getContents();
	    			StringBuilder whereQl = new StringBuilder(20);
	    			paramIndex = buildWhereQl(search, params, whereQl, paramIndex, entityManager);
	    			
	    			detailSearchQl.append(String.format(DTL_SEARCH_QUERY_STRING, mIdName, dIdName, 
	    					detailName, whereQl.toString()));	//" and %s = (select distinct %s from %s d where %s )";	//去除无用关联
	    		}
	    		search.setDetailSearchQl(detailSearchQl);
    		}
    		ql.append(detailSearchQl);
    	}
    }
    /**
     * 拼接明细SQL的where条件
     * @param search
     * @param params
     * @param whereQl
     * @param paramIndex
     * @param em 实体管理器
     * @return
     */
    @SuppressWarnings("unchecked")
	public int buildWhereQl(Searchable search, Map<String, Object> params, StringBuilder whereQl, 
    		int paramIndex, EntityManager em){
    	DtlCondition dtlCondition = new DtlCondition();
    	List<SearchFilter> dtlFilters = dtlCondition.getDtlFilters();
    	for(Map.Entry<String, Object> entry : params.entrySet()){
    		String key = entry.getKey();
    		Object value = entry.getValue();
    		if (value == null || "".equals(value.toString())) {
    			continue;
    		}
    		String spType= "=";
        	SearchOperator sp = SearchOperator.eq;
    		String propertyName = key;
    		int index = propertyName.indexOf("_");
    		if(index > -1){
    			propertyName = key.substring(0, index);
    			spType = SearchOperator.valueOf(key.substring(index+1)).getSymbol();
    			sp = SearchOperator.valueOf(key.substring(index+1));
    		}
    		
    		//增加物料编码查询的逻辑
    		if(propertyName.equals("materialCode")){
    			String s = "select m.materialNo from BasMaterial m where materialCode "+spType+" (?1)";
    			
    			Query q = em.createQuery(s);
    			q.setParameter(1, formtValueBySptype(spType, value));
    			List<Object> mList = q.getResultList();
    			if(Collections3.isNotEmpty(mList)){
    				propertyName = "materialNo";
    				spType = "in";
    				value = mList;
    				sp = SearchOperator.valueOf(spType);
    			}else{
    				whereQl.append(" and 1<>1 ");
    				return paramIndex;
    			}
    			
    		}
			whereQl.append(" and ").append(propertyName).append(" ").append(spType);
			if(spType.equals("in")){
				whereQl.append(" (:").append(paramPrefix).append(paramIndex).append(")");
			}else{
				whereQl.append(" :").append(paramPrefix).append(paramIndex);
			}
        	paramIndex ++;
        	dtlFilters.add(SearchFilterHelper.newCondition(propertyName, sp, value));
		}
    	
    	search.addSearchFilter(dtlCondition);
    	return paramIndex;
    }

	@Override
	public int prepareSQL(StringBuilder sql, Searchable search) {
		return prepareQLOrSQL(sql, search, true);
	}
	
	@Override
	public void prepareSQLOrder(StringBuilder sql, Searchable search) {
		prepareQlOrSqlOrder(sql, search, true);
    }
	
	private void prepareQlOrSqlOrder(StringBuilder qlOrSql, Searchable search, boolean isSQL) {
        if (search.hashSort()) {
            qlOrSql.append(" order by ");
            for (Sort.Order order : search.getSort()) {
                qlOrSql.append(String.format("%s%s %s, ", getAliasWithDot(), 
                		isSQL ? CommonUtils.convertJaveBeanStrToUnderLine(order.getProperty()):order.getProperty(), 
                		order.getDirection().name().toLowerCase()));
            }

            qlOrSql.delete(qlOrSql.length() - 2, qlOrSql.length());
        }
    }


}
